The Javascript APIs of gs5.js is grouped as following:
gs.version: SDK Version
The SDK version of SoftwareShield license kernel in string format.
gs.productName: Product Name
The string of product name defined in the SoftwareShield IDE.
This value is used by built-in UI template as Windows title.
When customizing the HTML page, you are free to ignore this value and use any hard-coded string displayed on the UI. For example, you can detect the current system default locale (locale support) and use an user-friendly string as product name.
gs.productId: Product ID
The unique string id of product-id defined in the SoftwareShield IDE.
gs.buildId: Product Build ID
The unique integer Build-ID of this application release.
var sdkVersion = gs.version; //sdkVersion = "5.3.2.0"
var appId = gs.productName + '(build: ' + gs.buildId + ', sdk version: ' + sdkVersion + ')';
alert(appId); // My Test Game (build: 8, sdk version: 5.3.2.0)
In SoftwareShield license project, you can define multiple entities to protect, each of them is associated with a license. SoftwareShield SDK encapsulates the license accessing apis in name space gs.core. The entities and licenses are mapped to corresponding Javascript objects so you can access their properties and member functons to access the current licensing status.
Enumerate All Entities
In Javascript, you can enumerate all defined entities as following:
for(i = 0; i < gs.core.getTotalEntityCount(); i++){
//get each entity object by its index
var entity = gs.core.getEntityByIndex(i);
Get Entity By EntityId
Given the unique entity-id defined in the license project, you can get the entity directly as following:
//get the entity object by its entity-id
var entity = gs.core.getEntityById('a4c33d4c-9239-4c66-899b-b6223ef40b72');
Single Entity: The most simple case
By default, SoftwareShield automatically defines an entity when creating a new project. If it suffices your requirement then there is only one entity in your license project. In this case, accessing this entity is quite easy:
//With gs.single helper
var entity = gs.single.getEntity();
//Single entity is the first entity with index = 0
var entity = gs.core.getEntityByIndex(0);
Current Entity
When an entity is to be accessed in code, the onStart UI page (onStart.htm by default) will pop up allowing customers to input license code if the entity is not activated yet; if there are multiple entities in your license project, how do we know which entity is being accessed? in SoftwareShield term, this entity causing the onStart UI launches is called
var entity = gs.core.getCurrentEntity();
Now that you have retrieved the entity object, you can access its information by reading its properties and calling its member functions.
Entity Properties
An entity object has the following properties:
var entityName = entity.name; // Entity name defined in license project
var entityId = entity.id; // Unique entity-id
var entityWhat = entity.what; //Long description of the entity
If your entity is associated with a trial license model (Expire by Access Times, Expire By Hard Date, Expire By Period, Expire by Duration, Expire by Session Time), you can test the entity's license status as following:
if(entity.isUnlocked()){
//this entity is already activated / fully purchased.
//app should run in full featured mode for this entity (game level, app module, etc.)...
run_in_full_featured_mode();
} else if (entity.isLocked()){
//this entity is locked or trial already expired.
//we may pop up info to prompt for purchase.
cannot_run();
} else {
//this entity still in trial period or duration
run_in_trial_mode();
}
If your entity is using License Model Always Lock, then the logic can be simplied as:
if(entity.isUnlocked()){
//this entity is already activated / fully purchased.
//app should run in full featured mode for this entity (game level, app module, etc.)...
run_in_full_featured_mode();
} else {
//this entity is still locked, we need a license to continue.
//we may pop up info to prompt for purchase.
cannot_run();
}
In the above section, we have detected that an entity is still in trial mode, so in order to display proper license information to end user on app UI, such as tell the game player how many trial time left before the game will stop running, we must be able to get more details of the license model.
var lic = entity.getLicense();
Now that the license object is ready, we can inspect it in various perspectives:
As a developer we have designed the license project in SoftwareShield IDE before, so we should have known which kind of license model being used for an entity. However, if you are developing a universal logic to parse any entity, it is helpful to find out the license model type being bundled with the input entity:
For example, for Expire By Period, its LicenseID is "gs.lm.expire.period.1", so by comparing the license id you can figure out the correct license model type to inspect further.
var licId = lic.id; //=> "gs.lm.expire.period.1"
var licName = lic.name; //=>"Expire By Period"
var licWhat = lic.what; //=> "Protected entity expires after it has been accessed more than a preset time period."
Now we have known which license model to inspect, let's retrieve the license parameters according to its model type:
This license model (Expire by Access Times) has two parameters of int type: "usedTimes" and "maxAccessTimes"
var usedTimes = lic.getParamValue("usedTimes");
var maxAccessTimes = lic.getParamValue("maxAccessTimes");
//How many times left the app can launch in trial mode?
var how_many_access_left = maxAccessTimes - usedTimes;
This model (Expire By Hard Date) is a bit complex since it have three different use cases, in this tutorial we only demonstrate the most commonly used one: "Expire After", in this case, the license has a predefined expiry date "timeEnd", the app trial mode expires after this date.
var expiryDate = lic.getParamValue("timeEnd");
console.log("Local: " + expiryDate); //=> "Wed Sep 30 2015 00:00:00 GMT-0700 (Pacific Daylight Time)"
console.log("UTC: " + expiryDate.toUTCString()); //=> "Wed, 30 Sep 2015 07:00:00 GMT"
The interest part is that SoftwareShield SDK api always returns UTC date time value, javascript is very easy to convert to local date time.
This license model (Expire By Period) has a UTC datetime parameter "timeFirstAccess", specify the timestamp the entity is first accessed, and an integer parameter "periodInSeconds", specify the total trial period allowed. The following code tries to figure out when the license will expire and how many trial time left before expire.
//Total trial period in seconds
var periodInSeconds = lic.getParamValue("periodInSeconds");
//Figure out when the entity is first accessed (aka. entity.beginAccess() is first called)
//if never accessed before, the _timeFirstAccess_ parameter won't hold a valid datetime and the api returns a null object
var timeFirstAccess = lic.getParamValue("timeFirstAccess");
if(timeFirstAccess != null){
//accessed before, so we can calculate the expiry date
var expiryDate = new Date(timeFirstAccess.getTime() + periodInSeconds * 1000);
//How many trial time left?
var now = new Date();
var timeLeftInSeconds = ( expiryDate - now )/1000;
} else {
//Never accessed, the expiry date is still unknown!
//How many trial time left = full trial period.
var timeLeftInSeconds = periodInSeconds;
}
The interesting part of the above code is that: if the "timeFirstAccess" does not hold a valid timestamp (the default value is null), which occurs when the entity is never accessed before, we cannot deduce the exact expiry date, however we can deduce how many time left before expire.
This license model (Expire by Duration) is simple, it just adds up all elapsed trial time cumulatively (in "usedDurationInSeconds") until the total trial time exceeds the pre-defined maximum value ("maxDurationInSeconds"). We cannot figure out the expiry date.
//Total trial period in seconds
var usedDurationInSeconds = lic.getParamValue("usedDurationInSeconds");
var maxDurationInSeconds = lic.getParamValue("maxDurationInSeconds");
//How many trial time left?
var timeLeftInSeconds = maxDurationInSeconds - usedDurationInSeconds;
This license model (Expire by Session Time) allows an entity can always be accessed (never expire) but in a limited time, we can get the session time left but cannot get the expiry date.
var sessionTimeUsed = lic.getParamValue("sessionTimeUsed");
var maxSessionTime = lic.getParamValue("maxSessionTime");
//How many trial time left?
var timeLeftInSeconds = maxSessionTime - sessionTimeUsed;
In the above section you have learned how to read license information for each license type, it is a quite common requirement that you want to display how many trial time left before expiration on your license UI, SDK/Javascript implements a member function of Entity object as a helper to let you retrieve the trial information easily.
var timeLeft = entity.getTimeLeft();
The returned timeLeft value can be interpreted per license type as following:
License Type | timeLeft Data Type | Memo |
---|---|---|
Expire by Access Times | integer | How many launch times left the app can run app in trial mode? |
Expire By Hard Date | float | How many trial time left (in seconds) before expiry date |
Expire By Period | float | How many trial time left (in seconds) before expiry date |
Expire by Duration | integer | How many trial time left (in seconds) before expiry date |
Expire by Session Time | integer | How many trial time left (in seconds) before the current app session expires |
While the Entity.getTimeLeft() is very handy to get trial information, its returned value has to be parsed according to different license type. SDK/Javascript also implements another useful member function of Entity object as a helper to parse the timeLeft value for you as simple strings.
var timeLeftStr = entity.getTimeLeftString();
var timeLeft = timeLeft.value;
The returned timeLeftStr object has three properties:
License Type | numberString | textString |
---|---|---|
Expire by Access Times | launch times left, ex: "1","8" | "Access Left", "Accesses Left" |
Expire By Hard Date, Expire By Period, Expire by Duration, Expire by Session Time | trial time left (in day/hour/minute/second), ex: "2" ,"30" | "Day(s) Left", "Hour(s) Left", "Minute(s) Left", "Second(s) Left" |
License Model Always Run | "--" | "Always Run" |
License Model Always Lock | "--" | "Always Locked" |
There are two methods to activate your app, online and offline.
After user've made purchase of your app, you send a serial number to the customer as a proof of valid customer and the key to activate your app.
if(core.isServerAlive()){
var success = gs.core.applySN(serial);
}else{
console.log("license server is not available!");
//optionally fall back to offline activation...
}
In order to do offline activation, we need to go through three steps:
var requestCode = gs.core.getDummyRequestCode();
We are using dummy request code because the license server can deduce all valid actions from the customer's serial number.
You display the request code on UI, asking the customer to contact your support team for a valid license code, or if possible, let the customer uses another machine (or a mobile device) to generate the license code by himself/herself via the app's public App Web Activator.
You can also develop a simple web page on your own web server, implementing the license code generating UI based on CheckPoint2 API (ref: License Code Generation).
Once the user input a license code, we can apply it to the app:
var licenseCode = readLicenseCodeFromUI(); //user input the license code.
var success = gs.core.applyLicenseCode(licenseCode, serial);
SoftwareShield SDK allows you to revoke serial numbers to license server so that the revoked serial numbers can be reused in another machine or the same machine (after hardware upgrade, system re-install, etc.)
var serialToRevoke;
var success = gs.core.revokeSN(serialToRevoke);
After successful revoke, all those entities previously unlocked by this serial are locked.
var success = gs.core.revokeApp();
After successful revoke, all previously unlocked entities are locked.
gs.app.rootPath : The full path to the app install directory.
gs.app.lmappPath : The full path to the directory the license UI files are installed.
gs.app.buyNowURL : The app's purchase URL.
In UI HTML/Javascript pages, you can control if the app should continue running, restart, or simply terminate immediately.
If your javascript logic detects the license is permitted, the app can run as following:
gs.app.play();
Result:
You may want to restart app when an license error is fixed or a serial number has been applied and your app needs restarting to run in full-featured mode:
gs.app.restart();
Result:
You can terminate app in Javascript as following:
var ec = 10; //app exit code
gs.app.exit(ec);
Result:
When app exiting from post-app UI, it terminates immediately, otherwise if this api is called from pre-app UI, the pre-defined app-exiting UI might pops up according to the project's UI Behavior.
The functions in this section allows you to fully control your license UI.
By default, the project's Product Name is used as UI title, however you can change the title as following:
gs.ui.setTitle('My App');
It is useful when you are developing a multilingual software that the UI should be localized according to the customer's active locale.
The maximum waiting time (in seconds) before the UI becomes visible. It makes sure the app get a visual feedback (even an incomplete UI content) instead of waiting for your javascript code executing or page loading endlessly, which might seem odd if the user can see nothing after the app icon is double-clicked.
//get the current load timeout value
var timeout = gs.ui.loadTimeOut();
//set the page loading timeout to 15 seconds
gs.ui.setLoadTimeOut(15);
The default value is 20 seconds. If your UI pages are deployed (or embedded) with app, it is good enough, if your UI pages are fetched from web server, the page loading timeout value is important. To get a better user experience it is better to use a hybrid scheme, the startup page template is a local one, its content can be downloaded from web server via AJAX after the UI becomes visible by calling gs.ui.show().
You can setup the default page load time out value in IDE (ref: Page Loading Time Out).
When user clicks the Buy Now link / button, you can redirect the customer to app's purchase web site (payment UI hosted in app store) in two ways:
$('buy-now-link')..on('click', function() {
window.location = "https://www.myapp.com/appstore/";
});
$('buy-now-link')..on('click', function() {
gs.util.launchDefaultBrowser("https://www.myapp.com/appstore/");
});
Since SDK v5.3.6, the BuyNowURL is saved in local license file, you can also enable "In-App Purchase" in UI Options dialog box:
As a result, you can unify the above two cases as following:
$('buy-now-link')..on('click', function() {
gs.ui.purchase();
});
The gs.ui.purchase() api will detect the current purchase settings ( BuyNowURL and In/Out-App Purchase) and renders the purchase UI properly.
Your javascript code runs in a context of HTML UI page, which can be visible, hidden or simply closed as following:
//show current UI
gs.ui.show();
When your javascript is running, the UI page is hidden to give you a chance to update the UI content. The page will become visible if either of the following cases occur:
//hide current UI
gs.ui.hide();
This api is used to hide the current UI, it is rarely used since the initial visibility of UI page is hidden, however you can call it explicitly in your code so that the UI will not show up after the loading timeout timer is elapsed --- it will be closed silently in this case.
//close current UI
gs.ui.close();
Normally the license UI pops up during a process of license event handling, when the UI is closed, the event handling continues. For example, the onStart.htm pops up when the auto-start entity is going to be accessed when app launching, it is your (javascript code's) responsibility to make sure the entity has a valid license before the UI is closed, you can detect the license status and prompt for app activation if necessary; after the UI is closed, SoftwareShield runtime will check if the entity's license is valid, if not, the app will not continue launching.
Usually you do not need to call this api, the gs.app.play() / gs.app.restart() /gs.app.exit() will close the UI automatically.
window.location = 'activate.htm';
//The remote page url defined in project settings, or you can use a hard-coded one as needed.
var urlbase = gs.ui.remoteUrlBase();
window.location = urlbase + 'activate.htm';
To jump from a page hosted on web server to a local page, we need a special api gs.ui.render()
gs.ui.render( filename, [optional] );
//The current page is a remote one, let's jump to local page...
gs.ui.render('activate.htm');
gs.ui.render() allows you specify some optional parameters for the page rendering:
gs.ui.render('activate.htm', { title: "App Activation" });
Location | Memo | Example |
---|---|---|
LOC_LOCAL | Use local UI page | gs.ui.render('activate.htm', {location: gs.ui.LOC_LOCAL}); |
LOC_REMOTE | Use remote UI page | gs.ui.render('activate.htm', {location: gs.ui.LOC_REMOTE}); |
LOC_AUTO (default) | Search the page according to project UI settings | gs.ui.render('activate.htm') |
SoftwareShield allows you to access client's operating system facilities in Javascript.
var osName = gs.os.name;
Possible name can be: "Windows", "Mac"
SoftwareShield has a built-in command line parameter "-showui", once specified, the javascript code in UI_HTML/onStart.htm is always executed on app launching even after the app is fully activated, so that you will have a chance to evaluate the current license status and allow customer to activate or transfer license.
//onStart.htm:
var args = gs.os.commandLineArgs;
if (arg[1] == "-showui") {
Shield.WindowVisible(true);
sessionStorage.setItem('showui', true);
if (args[2] == "-revoke") {
window.location = "licenseTransfer.htm";
return;
}else if (args[2] == "-activate") {
window.location = activateUrl;
return;
}
}
To simply force the javascript code of onStart.htm executing:
MyApp -showui
To launch directly to license transfer UI, the app is invoked as following:
MyApp -showui -revoke
To launch directly to license activation UI, the app is invoked as following:
MyApp -showui -activate
SoftwareShield allows you to access local file system from UI page, the following is the most commonly used directory apis:
API | Path | Example |
---|---|---|
gs.app.rootPath | Where is the app installed? | "C:\Users\randy\Documents\SoftwareShield\MyApp\Release\Deploy" |
gs.ui.localDir / gs.app.lmappPath | the absolute path to UI_HTML | "C:/Users/randy/Documents/SoftwareShield/MyApp/Release/Deploy/UI_HTML/" |
gs.os.homeDir | The current user's home directory | "C:\Users\randy" |
Directory APIs
API | Memo |
---|---|
gs.os.createDir( dir ) | Create a new directory, the input dir must be in absolute path format. |
gs.os.dirExists( dir ) | Test if a directory exists, the input dir must be in absolute path format. |
File APIs
API | Memo |
---|---|
gs.os.fileExists( file ) | Test if a file exists, the input file must be in absolute path format. |
gs.os.readTextFile( file ) | Read a text file and returns its content as a string. |
gs.os.writeTextFile( file, content ) | Write a string to a text file, its old content is replaced with the new content |
You can retrieve the value of environment variable in javascript as following:
//get system temporary directory
var tmpDir = gs.os.getEnvironmentVariable('TEMP');
//get the current user name
var username = gs.os.getEnvironmentVariable('USERNAME');
Copy to Clipboard:
//Generate a request code and copy it to local clipboard
var requestCode = gs.util.getUnlockRequestCode();
gs.os.clipboard.copy(requestCode);
Paste from Clipboard:
//Paste serial number from clipboard to UI page:
$('#serial').html( gs.os.clipboard.paste() );
To spawn the default e-mail client app:
var address = 'support@myapp.com';
var title = 'support request';
var requestCode = gs.util.getUnlockRequestCode();
var content = 'I want to manual activate my app, the offline activation request code is:' + requestCode;
gs.util.mailTo( address, title, content );
To launch the default web browser to a specific URL:
gs.util.launchDefaultBrowser('http://www.myapp.com');
To support multilingual UI SoftwareShield provides you the following properties:
API | Memo | Sample |
---|---|---|
gs.os.locale.language | Default language, ref: Country & Language | 'English' |
gs.os.locale.country | The customer's country, ref: Country & Language | 'Canada' |
gs.os.locale.BCP47Name | Default locale, ref: BCP47 Standard | 'en-CA', 'en-US', 'en-GB' |
Combined with the file access apis, you can develop your license UI speaking the customer's language:
The javascript does not provide a reliable api to detect if a key is pressed, it relies on the key down event after your HTML page (where the javascript is hosted) is loaded, so if you want to detect if the user is pressing "Shift" key when app is double-clicked, it cannot be done properly, so SoftwareShield implements the following api to allow you to query the current key status:
bool gs.os.keyboard.isModifierKeyPressed( mkey );
Checks if the modifier key is currently being pressed.
Supported modifier keys are:
Key | Memo | Windows | Mac |
---|---|---|---|
MKEY_SHIFT (1) | Shift Key | Yes | Yes |
MKEY_CONTROL (2) | Control Key | Yes | Yes |
MKEY_ALT (3) | Alt Key | Yes | Yes |
MKEY_WINDOWS (4) | Windows Key | Yes | No |
MKEY_COMMAND (5) | Command Key | No | Yes |
var isShiftKeyPressed = gs.os.keyboard.isModifierKeyPressed( gs.os.keyboard.MKEY_SHIFT );
SoftwareShield debug version will save detailed debug messages to a log file, you can use the following api to output debug messages to log file:
gs.core.trace('*** Hello World! ***');
This api does nothing for release version of binaries.
You can detect if you are running in an app wrapped by debug version of SoftwareShield:
var runningDebugVer = gs.core.isDebugVersion();
In previous overview section, we have mentioned you can output message in javascript standard console (ref: "Log Messages"), this message will not go into the debug log file.
Sometimes you might want to do some initialize task or display special messages on UI when the app is launched for the very first time. You can detect if the app is being launched for the first time as following:
if(gs.core.isAppFirstLaunched()){
alert('Hi, thanks for installation, ...');
}